﻿using System;
using Microsoft.Xna.Framework;

namespace RainEngine.Rendering
{
    /// <summary>
    /// 光源对象。一个光源包含颜色，光源类型，光线方向，光照范围和位置的信息。
    /// </summary>
    public sealed class Light 
    {
        /// <summary>
        /// 光源是否可用
        /// </summary>
        private bool enabled;        
        
        /// <summary>
        /// 光源颜色
        /// </summary>
        private Vector3 diffuseColor;
        
        /// <summary>
        /// 光线方向
        /// </summary>
        private Vector3 direction;        

        /// <summary>
        /// 光源位置
        /// </summary>
        private Vector3 position;        

        /// <summary>
        /// 聚光灯光源属性的数据集合，包括range, falloff, theta和phi
        /// </summary>
        public Vector4 SpotData;

        /// <summary>
        /// 光源类型枚举，分为单向光、点光源、聚光灯。
        /// </summary>
        private LightType lightType;        
       
        /// <summary>
        /// 创建一个单向光源。
        /// </summary>
        /// <param name="color">颜色</param>
        /// <param name="direction">光线方向</param>
        public Light(Vector3 color, Vector3 direction) : this(LightType.Directional, color, -direction, direction, 0, 0, 0, 0) 
        {
            if (direction == Vector3.Zero)
                throw new System.ArgumentException("单向光必须要有一个方向");
        }

        /// <summary>
        /// 创建一个点光源。
        /// </summary>
        /// <param name="setScene">所属场景</param>
        /// <param name="color">颜色</param>
        /// <param name="position">方向</param>
        /// <param name="range">光照范围</param>
        /// <param name="falloff">随距离变化的衰减因子，范围为0到无穷大，0表示不衰减，1表示线性减小</param>
        public Light(Vector3 color, Vector3 position, float range, float falloff) : this(LightType.Point, color, position, Vector3.Zero, range, falloff, 0, 0) { }

        /// <summary>
        /// 创建一个聚光灯光源。
        /// </summary> 
        /// <param name="color">颜色</param>
        /// <param name="position">位置</param>
        /// <param name="direction">方向</param>
        /// <param name="range">光照范围</param>
        /// <param name="falloff">在聚光灯光锥内部顶角和外部顶角之间的光照衰减因子，范围为0到无穷大，0表示不衰减，1表示线性减小。</param>
        /// <param name="theta">内光锥弧度，介于0值Phi之内。</param>
        /// <param name="phi">外光锥弧度，介于0至90之内。</param>
        public Light( Vector3 color, Vector3 position, Vector3 direction, float range, float falloff, float theta, float phi) : this(LightType.Spot, color, position, direction, range, falloff, theta, phi) { }

        /// <summary>
        /// 创建一个新Light对象。
        /// </summary>
        /// <param name="lightType">光源类型</param>
        /// <param name="color">光源颜色</param>
        /// <param name="position">光源位置</param>
        /// <param name="direction">光源方向</param>
        /// <param name="range">光照范围</param>
        /// <param name="fallof">聚光灯光锥内部顶角和外部顶角之间的光照衰减或点光源的距离衰减因子，范围为0到无穷大，0表示不衰减，1表示线性减小。</param>
        /// <param name="theta">聚光灯内光锥弧度，介于0值Phi之内。</param>
        /// <param name="phi">聚光灯外光锥弧度，介于0至90度之内。</param>
        public Light(LightType lightType, Vector3 color, Vector3 position, Vector3 direction, float range, float falloff, float theta, float phi)
        {
            this.enabled = true;
            this.lightType = lightType;            
            this.Color = color;
            this.Position = position;            
            this.Direction = direction;
            this.Enabled = true;
            this.SpotData.X = range;
            this.SpotData.Y = falloff;
            this.SpotData .Z=  theta;
            this.SpotData.W = phi;           
        }

        public delegate void LightPropertyChangedEventHandler(object sender, LightPropertyChangedEventArgs e);
        public event LightPropertyChangedEventHandler LightPropertyChanged;

        /// <summary>
        /// 获取光源类型。
        /// </summary>
        public LightType LightType 
        {
            get { return lightType; }
            set 
            { 
                lightType = value;
                if (LightPropertyChanged != null)
                {
                    LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("LightType");
                    LightPropertyChanged(this, e);
                }       
            }
        }

        /// <summary>
        /// 获取或设置光源是否可用
        /// </summary>
        public bool Enabled
        {
            get { return enabled; }
            set
            {
                if (enabled != value)
                {
                    enabled = value;
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e=new LightPropertyChangedEventArgs ("Enabled");
                        LightPropertyChanged(this, e);
                    }                        
                }
            }
        }          

        /// <summary>
        /// 获取或设置光源颜色。
        /// </summary>
        public Vector3 Color
        {
            get { return diffuseColor;}
            set 
            { 
                diffuseColor = value;
                if (LightPropertyChanged != null)
                {
                    LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("Color");
                    LightPropertyChanged(this, e);
                }
            }
        }

        /// <summary>
        /// 获取或设置光源位置，设置单向光的位置无意义，如果是单向光，则设置位置就是将方向设置为位置的反方向。
        /// </summary>
        public Vector3 Position
        {
            get{ return position;}
            set
            {
                if (lightType != LightType.Directional)
                {
                    position = value;
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("Position");
                        LightPropertyChanged(this, e);
                    }
                }
                else
                    direction = -position;
            }
        }

        /// <summary>
        /// 获取或设置光线方向，设置点光源的方向无意义。 
        /// </summary>
        public Vector3 Direction
        {
            get { return direction; }
            set
            {
                if (lightType != LightType.Point)
                {
                    direction = value;
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("Direction");
                        LightPropertyChanged(this, e);
                    }
                }
            }
        }

        /// <summary>
        /// 获取或设置光源作用距离。这个参数对单向光不起作用。
        /// </summary>
        public float Range
        {
            get { return SpotData.X;}
            set 
            {
                if (lightType != LightType.Directional)
                {
                    if (value < 0)
                        value = 0;
                    SpotData.X = value;
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("SpotData");
                        LightPropertyChanged(this, e);
                    }
                }
            }
        }

        /// <summary>
        /// 获取或设置聚光灯光锥内部顶角和外部顶角之间的光照衰减或点光源的距离衰减因子
        /// </summary>
        public float FallOff
        {
            get { return SpotData.Y; }
            set 
            {
                if (lightType != LightType.Directional)
                {
                    if (value < 0)
                        value = 0;
                    SpotData.Y = value;
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("SpotData");
                        LightPropertyChanged(this, e);
                    }
                }
            }
        }

        /// <summary>
        /// 获取或设置聚光灯内光锥角度，介于0和Phi之内。
        /// </summary>
        public float Theta
        {
            get { return SpotData .Z; }
            set 
            {
                if (lightType == LightType.Spot)
                {
                    if (value < 0)
                        value = 0;
                    else if (value > SpotData.W)
                        value = SpotData.W;
                    SpotData.Z = (float)Math.Cos(MathHelper.ToRadians(value));
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("SpotData");
                        LightPropertyChanged(this, e);
                    }
                }
            }
        }

        /// <summary>
        /// 获取或设置聚光灯外光锥角度，介于0至90度之内。
        /// </summary>
        public float Phi
        {
            get { return SpotData.W; }
            set 
            {
                if (lightType == LightType.Spot)
                {
                    if (value < 0)
                        value = 0;
                    else if (value > 90)
                        value = 90;
                    SpotData.W = (float)Math.Cos(MathHelper.ToRadians(value));
                    if (LightPropertyChanged != null)
                    {
                        LightPropertyChangedEventArgs e = new LightPropertyChangedEventArgs("SpotData");
                        LightPropertyChanged(this, e);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 自定义事件参数类，包含了字符串LightProperty作为参数
    /// </summary>
    public class LightPropertyChangedEventArgs : EventArgs
    {
        private string lightProperty;

        //本属性用于传递事件数据
        public string LightProperty
        {
            get { return lightProperty; }
        }

        public LightPropertyChangedEventArgs(string setLightProperty)
        {
            lightProperty = setLightProperty;
        }
    }


    /// <summary>
    /// 光源类型枚举。
    /// </summary>
    public enum LightType
    {
        /// <summary>
        /// 最常用的光源。类似于太阳光，因为光源在无穷远处，所以场景中的所有物体都会被这个光源照亮。
        /// </summary>
        Directional,

        /// <summary>
        /// 向四周发射光线的点光源，Direction参数对这个光源无意义。
        /// </summary>
        Point,
        
        /// <summary>
        /// 聚光灯。
        /// </summary>
        Spot,
    }
}
